package org.dinqy.impl;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;

import org.dinqy.ClauseBean;
import org.dinqy.QueryBean;
import org.dinqy.QueryBuilder;
import org.dinqy.QueryExecutor;

/**
 * @author armandino (at) gmail.com
 */
public final class QueryExecutorImpl<T> implements QueryExecutor<T> {
    private final EntityManager entityManager;
    
    public QueryExecutorImpl(ClauseNode<?> rootNode, EntityManager entityManager) {
        this.entityManager = entityManager;
    }
    
    @SuppressWarnings("unchecked")
    public T result(Class<T> entityClass, List<ClauseBean> clauseBeans) {
        try {
            return (T)createQuery(entityClass, clauseBeans).getSingleResult();
        }
        catch(NoResultException ex) {
            return null;
        }
    }
    
    @SuppressWarnings("unchecked")
    public List<T> list(Class<T> entityClass, List<ClauseBean> clauseBeans) {
        return createQuery(entityClass, clauseBeans).getResultList();
    }

    @Override
    public long count(Class<T> entityClass, List<ClauseBean> clauseBeans) {
        return (Long)createCountQuery(entityClass, clauseBeans).getSingleResult();
    }
    
    private Query createQuery(Class<T> entityClass, List<ClauseBean> clauseBeans) {
        QueryBuilder queryBuilder = new QueryBuilderImpl(entityClass, clauseBeans);
        return buildJPAQuery(queryBuilder.getQueryBean());
    }
    
    private Query createCountQuery(Class<T> entityClass, List<ClauseBean> clauseBeans) {
        QueryBuilder queryBuilder = new QueryBuilderImpl(entityClass, clauseBeans);
        return buildJPAQuery(queryBuilder.getCountQueryBean());
    }
    
    private Query buildJPAQuery(QueryBean queryBean) {
        final Query query = entityManager.createQuery(queryBean.getQuery());
        final List<Object> queryArguments = queryBean.getQueryArguments();
        
        for(int i=0; i < queryArguments.size(); i++) {
            final int queryParameterIndex = i+1; // JPA counts from 1
            query.setParameter(queryParameterIndex, queryArguments.get(i));
        }
        
        if(queryBean.hasLimit())
            query.setMaxResults(queryBean.getLimit());
        if(queryBean.hasOffset())
            query.setFirstResult(queryBean.getOffset());
        
        return query;
    }
    
}
